﻿#if UNITY_EDITOR

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace AI.Graph.Editor {


    public static class GUIScaler {

        private const float m_editorTabHeightDocked = 19F;
        private const float m_editorTabHeightNotDocked = 23;
        private static readonly Stack<Matrix4x4> matrixStack = new Stack<Matrix4x4> ();
        private static readonly Stack<bool> m_editorWindowDockedStack = new Stack<bool> ();


        /// <summary>
        /// Creates a scaled rect and must be end with EndScaleArea.
        /// </summary>
        /// <param name="screenArea">The whole are.</param>
        /// <param name="offset">The rendered offset.</param>
        /// <param name="zoom">The current zoom level</param>
        /// <param name="isEditorWindowDocked">Is the EditorWindow docked.</param>
        /// <returns></returns>
        public static Rect BeginScaleArea (Rect screenArea, Vector2 offset, float zoom, bool isEditorWindowDocked = false) {
            m_editorWindowDockedStack.Push (isEditorWindowDocked);

            // end Unity's GUI group.
            GUI.EndGroup ();

            Rect scaledRect = Scale (screenArea, 1 / zoom, screenArea.min);
            scaledRect.y += isEditorWindowDocked ? m_editorTabHeightDocked : m_editorTabHeightNotDocked;

            GUI.BeginClip (scaledRect, -offset, Vector2.zero, false);

            matrixStack.Push (GUI.matrix);
            Matrix4x4 t = Matrix4x4.TRS (scaledRect.min, Quaternion.identity, Vector3.one);
            Matrix4x4 s = Matrix4x4.Scale (new Vector3 (zoom, zoom, 1));
            GUI.matrix = t * s * t.inverse * GUI.matrix;

            return scaledRect;
        }


        /// <summary>
        /// Creates a scaled rect and must be end with EndScaleArea.
        /// </summary>
        /// <param name="screenArea">The whole are.</param>
        /// <param name="offset">The rendered offset.</param>
        /// <param name="zoom">The current zoom level</param>
        /// <param name="editorWindow">The EditorWindow is used to check if the window is docked.</param>
        /// <returns></returns>
        public static Rect BeginScaleArea (Rect screenArea, Vector2 offset, float zoom, EditorWindow editorWindow = null) {
            return BeginScaleArea (screenArea, offset, zoom, editorWindow != null && editorWindow.IsEditorWindowDocked ());
        }


        /// <summary>
        /// End the scale area.
        /// </summary>
        public static void EndScaleArea () {
            GUI.EndClip ();
            GUI.matrix = matrixStack.Pop ();

            float tabHeight = m_editorWindowDockedStack.Pop () ? m_editorTabHeightDocked : m_editorTabHeightNotDocked;

            // Begin Unity's GUI group again.
            GUI.BeginGroup (new Rect (0, tabHeight, Screen.width, Screen.height - tabHeight));
        }


        public static Vector2 UnscaledToScaledPoint (Vector2 unscaledPoint, Rect unscaledRect, Vector2 offset, float zoom) {
            return (unscaledPoint - unscaledRect.min) / zoom + offset;
        }


        public static Vector2 ScaledToUnscaledPoint (Vector2 scaledPoint, Rect scaledRect, Vector2 offset, float zoom, bool isEditorWindowDocked = false) {
            Vector2 correctScaledRectPosition = scaledRect.min - new Vector2 (0, isEditorWindowDocked ? m_editorTabHeightDocked - 1 : m_editorTabHeightNotDocked - 1);
            return (scaledPoint - offset) * zoom + correctScaledRectPosition;
        }


        public static Vector2 ScaledToUnscaledPoint (Vector2 scaledPoint, Rect scaledRect, Vector2 offset, float zoom, EditorWindow editorWindow = null) {
            return ScaledToUnscaledPoint (scaledPoint, scaledRect, offset, zoom, editorWindow != null && editorWindow.IsEditorWindowDocked ());
        }


        static Rect Scale (Rect rect, float scale, Vector2 pivotPoint) {
            Rect result = rect;
            result.x -= pivotPoint.x;
            result.y -= pivotPoint.y;
            result.xMin *= scale;
            result.xMax *= scale;
            result.yMin *= scale;
            result.yMax *= scale;
            result.x += pivotPoint.x;
            result.y += pivotPoint.y;
            return result;
        }

    }


}
#endif